/*
* Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
* Description: main file
*/
#include <sys/time.h>
#include <unistd.h>
#include <cstdlib>
#include <opencv2/core/core.hpp>
#include <opencv2/core/utils/filesystem.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <vector>
#include <csignal>
#include <memory>
#include <boost/algorithm/string.hpp>
#include "pcd_sender.h"
#include "ara/rm/rm.h"

#include "decoded_mbuf_image_sender.h"
#include "decoded_mbuf_video_sender.h"
#include "decoded_mbuf_yuv_sender.h"
#include "image_mbuf_image_sender.h"
#include "image_mbuf_yuv_sender.h"
#include "image_mbuf_video_sender.h"
#include "util.h"

using namespace std;
using namespace cv;

int32_t g_stopFlag = 0;

void SetEnv()
{
    if (comPara.fileType == "pcd") {
        setenv("CM_CONFIG_FILE_PATH", "/opt/platform/mdc_platform/manual_service/lidar_a_cm/etc/LidarCmProcess", 1);
    } else {
        setenv("CM_CONFIG_FILE_PATH", "/opt/platform/mdc_platform/manual_service/camera_a_cm/etc/CameraProcess", 1);
    }
}

inline bool IsInstanceIdAvialable(std::string fileType, int32_t id)
{
    bool idIsAvailable = false;
    if (fileType == "pcd") {
        // 3为lidar的InstanceId最小值 7为lidar的InstanceId最大值
        if (id >= 3 && id <= 7) {
            idIsAvailable = true;
            return idIsAvailable;
        }
        std::cout << "current " << fileType << " instanceId is " << id << ",instanceId error,available value is [3,7]" << std::endl;
        return idIsAvailable;
    }
    // 21为camera的InstanceId最小值 35为camera的InstanceId最大值
    if (id >= 21 && id <= 35 && comPara.transMode != "image") {
        idIsAvailable = true;
        return idIsAvailable;
    }
    if (id >= 1021 && id <= 1034 && comPara.transMode == "image") {
        idIsAvailable = true;
        return idIsAvailable;
    }
    std::cout << "current " << fileType << " instanceId is " << id << ",instanceId error！" << std::endl;
    std::cout << "cameraDecodedMbufEvent available instanceId avaiable is [21,35]." << std::endl;
    std::cout << "cameraImageMbufEvent available instanceId avaiable is [1021,1034]." << std::endl;
    return idIsAvailable;
}

bool IsDateTimeAvailabe(string &dateTime)
{
    vector<string> dateStringVec;
    boost::split(dateStringVec, dateTime, boost::is_any_of(" "), boost::token_compress_on);
    if (dateStringVec.size() != 2) {
        std::cout << "the input datetime format is error,usage \'2022-02-18 10:09:08\'" << std::endl;
        return false;
    }
    vector<string> timeStringVec;
    boost::split(timeStringVec, dateStringVec[1], boost::is_any_of(":"), boost::token_compress_on);
    if (timeStringVec.size() != 3) {
        std::cout << "the input datetime format is error,usage \'2022-02-18 10:09:08\'" << std::endl;
        return false;
    }
    uint32_t hour = atoi(timeStringVec[0].c_str());
    uint32_t minutes = atoi(timeStringVec[1].c_str());
    uint32_t seconds = atoi(timeStringVec[2].c_str());
    if (hour >= 24 || minutes >= 60 || seconds >= 60) {
        std::cout << "the input datetime format is error,usage \'2022-02-18 10:09:08\'" << std::endl;
        return false;
    }
    return true;
}

int main(int32_t argc, char *argv[])
{
    int32_t ch;
    while ((ch = getopt(argc, argv, "c:f:hlm:r:t:")) != -1) {
        switch (ch) {
        case 'c':
            comPara.configFile = optarg;
            break;
        case 'f':
            comPara.fileType = optarg;
            break;
        case 'h':
            printf("--usage: \n");
            printf("./local_sensor_simulation -f [fileType] -c [configFile] -m [transMode] \n");
            printf("for example: ./local_sensor_simulation -f jpg -c image_config.yaml -m decoded -r 30 \n");
            printf("\n");
            printf("--option: \n");
            printf("  -f : [jpg/jpeg/yuv/h264/h265/pcd], requirement \n");
            printf("  -c : [configFile], requirement \n");
            printf("  -m : [decoded/image/simple/standard], option \n");
            printf("  -t : [yyyy-MM-dd HH:mm:ss], option \n");
            printf("  -r : [hz], option \n");
            printf("  -l , for loop, option \n");
            printf("  -h , for help, option \n");
            return 0;
        case 'l':
            comPara.loop = true;
            break;
        case 'm':
            comPara.transMode = optarg;
            break;
        case 'r':
            comPara.frequency = optarg;
            break;
        case 't':
            comPara.time = optarg;
            break;
        case '?':
            printf("miss para!\n");
            break;
        case ':':
            printf("invalid para! please input '-h' for help\n");
            break;
        }
    }
    if (comPara.fileType == "") {
        std::cout << "'-f File type' can not be empty!" << std::endl;
        return 1;
    } else {
        if (comPara.fileType != "jpg" && comPara.fileType != "jpeg" && comPara.fileType != "yuv" &&
                comPara.fileType != "h264" && comPara.fileType != "h265" && comPara.fileType != "pcd") {
            std::cout << "-f File type is error: " << comPara.fileType << std::endl;
            return 1;
        }
    }
    if (comPara.configFile == "") {
        std::cout << "'-c Config file' can not be empty!" << std::endl;
        return 1;
    }
    //校验时间
    std::string timeSample = "2022-02-18 10:09:08";
    try {
        if (!comPara.time.empty()) {
            if (comPara.time.length() != timeSample.length()) {
                std::cout << "the input datetime format is error,usage \'" << timeSample << "\'" << std::endl;
                return -1;
            }
            if (!IsDateTimeAvailabe(comPara.time)) {
                return -1;
            }
            boost::gregorian::date dateTime = boost::gregorian::from_string(comPara.time);
        }
    } catch (std::exception &e) {
        std::cout << "the input datetime format is error,usage \'" << timeSample << "\'" << std::endl;
        return -1;
    }
    // 配置文件不存在则退出
    if (!ConfigMangage::FileExist(comPara.configFile)) {
        std::cout << comPara.configFile << " file is not exist!" << std::endl;
        return 1;
    }
    if (comPara.frequency != "") {
        int32_t frequency  = atoi(comPara.frequency.c_str());
        if (frequency <= 0 || frequency > 100) {
            std::cout << "-r frequency must be (0 < frequency <= 100)" << std::endl;
            return 1;
        }
    }
    if (comPara.transMode != "") {
        if (comPara.transMode != "decoded" && comPara.transMode != "image" && comPara.transMode != "standard" && comPara.transMode != "simple") {
            std::cout << "-m transfer mode is error:" << comPara.transMode << std::endl;
        }
    }
    ConfigMangage configManage{comPara.configFile};
    configManage.Parse();
    cout << "parse finished. [main]" << endl;
    configManage.Show();
    if (!configManage.CheckConfig()) {
        cerr << "Config check failed." << endl;
        return 1;
    }
    auto configs = configManage.GetConfig();
    if (!ara::rm::RegisterHisiResource()) {
        std::cerr << "Register process to RM failed! Check if RM has been started." << std::endl;
        return -1;
    }
    vector<shared_ptr<MbufImageSender>> mbufSenders;
    vector<shared_ptr<MbufVideoSender>> mbufVideoSenders;
    vector<shared_ptr<MbufYuvSender>> mbufYuvSenders;
    vector<shared_ptr<PcdSender>> pcdSenders;
    vector<shared_ptr<ImageMbufImageSender>> imageMbufSenders;
    vector<shared_ptr<ImageMbufYuvSender>> imageMbufYuvSenders;
    vector<shared_ptr<ImageMbufVideoSender>> imageMbufVideoSenders;
    if (comPara.transMode == "image") {
        for (auto &conf : configs) {
            std::vector<std::string> files;
            int32_t fileCnt = getFilesBySort(conf.dir, files, comPara.fileType);
            if (fileCnt == 0) {
                std::cout << "dir : " << conf.dir << " has no " << comPara.fileType << " file" << std::endl;
                continue;
            }
            // 校验instanceId
            if (!IsInstanceIdAvialable(comPara.fileType, conf.instanceId)) {
                continue;
            }
            if (comPara.fileType == "jpg" || comPara.fileType == "jpeg") {
                imageMbufSenders.push_back(make_shared<ImageMbufImageSender>(conf.instanceId, Size{conf.width, conf.height}, conf.dir));
            }
            if (comPara.fileType == "h264" || comPara.fileType == "h265") {
                imageMbufVideoSenders.push_back(make_shared<ImageMbufVideoSender>(conf.instanceId, Size{conf.width, conf.height}, conf.dir));
            }
            if (comPara.fileType == "yuv") {
                imageMbufYuvSenders.push_back(make_shared<ImageMbufYuvSender>(conf.instanceId, Size{conf.width, conf.height}, conf.dir));
            }
            if (comPara.fileType == "pcd") {
                pcdSenders.push_back(make_shared<PcdSender>(conf.instanceId, Size{conf.width, conf.height}, conf.dir));
            }
        }
    } else {
        for (auto &conf : configs) {
            std::vector<std::string> files;
            int32_t fileCnt = getFilesBySort(conf.dir, files, comPara.fileType);
            if (fileCnt == 0) {
                std::cout << "dir : " << conf.dir << " has no " << comPara.fileType << " file" << std::endl;
                continue;
            }
            // 校验instanceId
            if (!IsInstanceIdAvialable(comPara.fileType, conf.instanceId)) {
                continue;
            }
            if (comPara.fileType == "jpg" || comPara.fileType == "jpeg") {
                mbufSenders.push_back(make_shared<MbufImageSender>(conf.instanceId, Size{conf.width, conf.height}, conf.dir));
            }
            if (comPara.fileType == "h264" || comPara.fileType == "h265") {
                mbufVideoSenders.push_back(make_shared<MbufVideoSender>(conf.instanceId, Size{conf.width, conf.height}, conf.dir));
            }
            if (comPara.fileType == "yuv") {
                mbufYuvSenders.push_back(make_shared<MbufYuvSender>(conf.instanceId, Size{conf.width, conf.height}, conf.dir));
            }
            if (comPara.fileType == "pcd") {
                pcdSenders.push_back(make_shared<PcdSender>(conf.instanceId, Size{conf.width, conf.height}, conf.dir));
            }
        }
    }
    SetEnv();
    for (auto &send : mbufSenders) {
        send->RegisterSender();
        if (send->BufCreatePool(2880 * 1856 * 3, 20) == 0) {
            std::cout << "INFO: send->BufCreatePool succeed one!" << std::endl;
        } else {
            std::cout << "ERROR: send->BufCreatePool failed!" << std::endl;
            return -1;
        }
    }
    for (auto &videoSend : mbufVideoSenders) {
        videoSend->RegisterSender();
        if (videoSend->BufCreatePool(2880 * 1856 * 3, 20) == 0) {
            std::cout << "INFO: send->BufCreatePool succeed one!" << std::endl;
        } else {
            std::cout << "ERROR: send->BufCreatePool failed!" << std::endl;
            return -1;
        }
    }
    for (auto &yuvSend : mbufYuvSenders) {
        yuvSend->RegisterSender();
        if (yuvSend->BufCreatePool(2880 * 1856 * 3, 20) == 0) {
            std::cout << "INFO: send->BufCreatePool succeed one!" << std::endl;
        } else {
            std::cout << "ERROR: send->BufCreatePool failed!" << std::endl;
            return -1;
        }
    }
    for (auto &pcdSend : pcdSenders) {
        pcdSend->RegisterSender();
    }
    for (auto &imageSend : imageMbufSenders) {
        imageSend->RegisterSender();
        if (imageSend->BufCreatePool(imageSend->imgSize.width * imageSend->imgSize.height * 3 / 2, 40) == 0) {
            std::cout << "INFO: send->BufCreatePool succeed one!" << std::endl;
        } else {
            std::cout << "ERROR: send->BufCreatePool failed!" << std::endl;
            return -1;
        }
    }
    for (auto &imageYuvSend : imageMbufYuvSenders) {
        imageYuvSend->RegisterSender();
        if (imageYuvSend->BufCreatePool(imageYuvSend->imgSize.width * imageYuvSend->imgSize.height * 3 / 2, 40) == 0) {
            std::cout << "INFO: send->BufCreatePool succeed one!" << std::endl;
        } else {
            std::cout << "ERROR: send->BufCreatePool failed!" << std::endl;
            return -1;
        }
    }
    for (auto &imageVideoSend : imageMbufVideoSenders) {
        imageVideoSend->RegisterSender();
        if (imageVideoSend->BufCreatePool(imageVideoSend->imgSize.width * imageVideoSend->imgSize.height * 3 / 2, 40) == 0) {
            std::cout << "INFO: send->BufCreatePool succeed one!" << std::endl;
        } else {
            std::cout << "ERROR: send->BufCreatePool failed!" << std::endl;
            return -1;
        }
    }
    usleep(1000000);
    vector<thread> pool;
    for (auto &send : mbufSenders) {
        pool.push_back(thread(&MbufImageSender::FillImageAndSend, send, "", 0, &g_stopFlag));
    }
    for (auto &videoSend : mbufVideoSenders) {
        videoSend->VdecDecoded();
    }
    for (auto &yuvSend : mbufYuvSenders) {
        pool.push_back(thread(&MbufYuvSender::FillImageAndSend, yuvSend, "", 0, &g_stopFlag));
    }
    for (auto &pcdSend : pcdSenders) {
        pool.push_back(thread(&PcdSender::FillImageAndSend, pcdSend, "", 0, &g_stopFlag));
    }
    for (auto &imageSend : imageMbufSenders) {
        pool.push_back(thread(&ImageMbufImageSender::FillImageAndSend, imageSend, "", 0, &g_stopFlag));
    }
    for (auto &imageYuvSend : imageMbufYuvSenders) {
        pool.push_back(thread(&ImageMbufYuvSender::FillImageAndSend, imageYuvSend, "", 0, &g_stopFlag));
    }
    for (auto &imageVideoSend : imageMbufVideoSenders) {
        imageVideoSend->VdecDecoded();
    }
    for (auto &t : pool) {
        if (t.joinable()) {
            t.join();
        }
    }
    return 0;
}
